#pragma once

#include <gtl/io/archive.h>
#include <gtl/type/traits.h>

namespace gtl
{

class any
{
	class value
	{
	public:
		virtual ~value()
		{
		}

	public:
		virtual value* clone() = 0;
		virtual const std::type_info& type() const = 0;
		virtual bool operator==(const value& val) = 0;

	public:
		virtual archive& operator<<(archive& ar) = 0;
		virtual archive& operator>>(archive& ar) = 0;
		//virtual oarchive& operator<<(oarchive& ar) = 0;
		//virtual iarchive& operator>>(iarchive& ar) = 0;
	};

	template<class T>
	class holder : public value
	{
	public:
		holder(const T& val)
			: m_val(val)
		{
		}

		virtual ~holder()
		{
		}

	public:
		value* clone()
		{
			return new holder(m_val);
		}

		virtual const std::type_info& type() const
		{
			return typeid(T);
		}

		virtual bool operator==(const value& val)
		{
			if(type() != val.type())
				return false;

			return m_val == static_cast<const holder<T>&>(val).m_val;
		}

	public:
		virtual archive& operator<<(archive& ar)
		{
			////ar << m_val;
			return ar;
		}

		virtual archive& operator>>(archive& ar)
		{
			////ar >> m_val;
			return ar;
		}

		//virtual oarchive& operator<<(oarchive& ar)
		//{
		//	ar << m_val;
		//	return ar;
		//}

		//virtual iarchive& operator>>(iarchive& ar)
		//{
		//	ar >> m_val;
		//	return ar;
		//}

	public:
		T m_val;
	};

public:
	any()
	{
		m_val = NULL;
	}

	template<class T>
	explicit any(const T& val)
	{
		m_val = new holder<T>(val);
	}

	any(const any& val)
	{
		m_val = NULL;
		if(val.m_val == NULL)
			return;

		m_val = val.m_val->clone();
	}

	any(any&& val)
	{
		m_val = val.m_val;
		val.m_val = NULL;
	}

	~any()
	{
		release();
	}

public:
	template<class T>
	T get() throw(std::bad_cast)
	{
		typedef gtl::type::traits<T>::value_type nonref;
		if(typeid(nonref) != m_val->type())
			throw(std::bad_cast());

		return static_cast<holder<nonref>*>(m_val)->m_val;
	}

public:
	template<class T>
	any& operator=(const T& val)
	{
		if(m_val != NULL)
			delete m_val;

		m_val = new holder<T>(val);
		return *this;
	}

	any& operator=(const any& val)
	{
		release();
		if(val.m_val == NULL)
			return *this;

		m_val = val.m_val->clone();
		return *this;
	}

	any& operator=(any&& val)
	{
		release();
		m_val = val.m_val;
		val.m_val = NULL;
		return *this;
	}

	bool operator==(const any& val) const
	{
		if(m_val == NULL || val.m_val == NULL)
			return false;

		return *m_val == *val.m_val;
	}

	const std::type_info& type()
	{
		return m_val != NULL ? m_val->type() : typeid(void);
	}

public:
	template<class Archive>
	void serialize(Archive& ar)
	{
		if(m_val == NULL)
			return;

		ar & (*m_val);
	}

	template<class Archive>
	friend Archive& operator<<(Archive& ar, const any& val)
	{
		if(val.m_val == NULL)
			return ar;

		val.m_val->operator<<(ar);
		return ar;
	}

	template<class Archive>
	friend Archive& operator>>(Archive& ar, any& val)
	{
		if(val.m_val == NULL)
			return ar;

		val.m_val->operator>>(ar);
		return ar;
	}

protected:
	void release()
	{
		if(m_val != NULL)
		{
			delete m_val;
			m_val = NULL;
		}
	}

protected:
	value* m_val;
};

} // end of namespace gtl